package com.ztk.threadbasic.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.ztk.threadbasic.enums.ExcelEnum;
import com.ztk.threadbasic.excel.CompanyVO;
import com.ztk.threadbasic.excel.handler.*;
import com.ztk.threadbasic.excel.listener.CustomReadListener;
import com.ztk.threadbasic.mapper.ExcelMapper;
import com.ztk.threadbasic.result.ExcelResult;
import com.ztk.threadbasic.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;

@Slf4j
@Service
public class ExcelServiceImpl implements ExcelService {

    @Autowired
    private ExcelMapper excelMapper;

    // 默认最多读取excel中的前3个sheet页，可通过配置修改该值
    @Value("#{T(java.lang.Integer).parseInt('${read.excel.sheet.count:2}')}")
    private int SHEET_SIZE;

    @Value("#{T(java.lang.Integer).parseInt('${read.excel.batch.inset.size:100}')}")
    private int BATCH_SIZE;

    @Value("${write.excel.file.name:预算监测表}")
    private String writeFileName;

    private String downloadTemplatePath = "template/excel/budgetMonitor_V2.xlsx";

    @Override
    public ExcelResult uploadExcel(MultipartFile file) {
        ExcelReader excelReader = null;
        ExcelResult excelResult = new ExcelResult();
        try (InputStream fileInputStream = file.getInputStream()) {
            log.info("读取excel开始!!!");

            excelReader = EasyExcel.read(fileInputStream).build();
            List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
            if (CollectionUtils.isEmpty(readSheets)) {
                log.info("文件无内容，读取结束!!!");
                excelResult.setStatus(ExcelEnum.FILE_EMPTY.getCode());
                excelResult.setSuccess(true);
                excelResult.setMessage(ExcelEnum.FILE_EMPTY.getMessage());
                return excelResult;
            }

            int fileSheetCount = readSheets.size() > SHEET_SIZE ? SHEET_SIZE : readSheets.size();
            // 逐页获取sheet页数量
            dealSheet(excelReader, fileSheetCount);

            return excelResult;
        } catch (Exception e) {
            log.error("上传excel异常!!!");
            log.error(e.getMessage(), e);
            excelResult.setStatus(ExcelEnum.PARSE_FAILURE.getCode());
            excelResult.setSuccess(Boolean.FALSE);
            excelResult.setMessage(ExcelEnum.PARSE_FAILURE.getMessage());
            return excelResult;
        } finally {
            if (!Objects.isNull(excelReader)) {
                excelReader.finish();
            }
            log.info("读取Excel结束!!!");
        }
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadExcel(HttpServletResponse response, String budgetAgency, String budgetDate) {
        ExcelResult excelResult = new ExcelResult();
        InputStream templateExcelInputStream = null;
        ExcelWriter excelWriter = null;
        try {
            // 基于条件，查询是否存在导出数据
            int count = excelMapper.existsDownloadData(budgetAgency, budgetDate);
            log.info("基于预算机构：{}，预算时间：{}，查询结果数量为：{}", budgetAgency, budgetDate, count);
            if (count == 0) {
                excelResult.setStatus(ExcelEnum.DOWANLOAD_EMPTY.getCode());
                excelResult.setMessage(ExcelEnum.DOWANLOAD_EMPTY.getMessage());
                return ResponseEntity.status(ExcelEnum.DOWANLOAD_EMPTY.getCode()).build();
            }

            templateExcelInputStream = this.getClass().getClassLoader().getResourceAsStream(downloadTemplatePath);

            if (Objects.isNull(templateExcelInputStream)) {
                // 获取模板为空，则流程结束
                excelResult.setStatus(ExcelEnum.DOWNLOAD_TEMPLATE_EMPTY.getCode());
                excelResult.setSuccess(Boolean.FALSE);
                excelResult.setMessage(ExcelEnum.DOWNLOAD_TEMPLATE_EMPTY.getMessage());
                return ResponseEntity.status(ExcelEnum.DOWNLOAD_TEMPLATE_EMPTY.getCode()).build();
            }

            List<CompanyVO> companyVOS = excelMapper.selectDownloadData(budgetAgency, budgetDate);

            excelWriter = EasyExcel.write(response.getOutputStream(), CompanyVO.class)
                    .withTemplate(templateExcelInputStream)
                    .build();

            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            Map<String, String> firstLineData = new HashMap<>();
            firstLineData.put("budgetAgency", budgetAgency);
            firstLineData.put("budgetDate", budgetDate);
            excelWriter.fill(firstLineData, writeSheet);

            List<CompanyVO> list1 = companyVOS.subList(0, 18);
            excelWriter.fill(new FillWrapper("integer", list1), writeSheet);

            List<CompanyVO> list2 = companyVOS.subList(18, companyVOS.size());
            excelWriter.fill(new FillWrapper("percent", list2), writeSheet);

            String fileName = URLEncoder.encode(writeFileName, "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            ServletOutputStream outputStream = response.getOutputStream();

            return ResponseEntity.status(ExcelEnum.OK.getCode()).build();
        } catch (Exception e) {
            log.error("下载excel异常!!!");
            log.error(e.getMessage(), e);
            excelResult.setStatus(ExcelEnum.DOWNLOAD_FAILURE.getCode());
            excelResult.setSuccess(Boolean.FALSE);
            excelResult.setMessage(ExcelEnum.DOWNLOAD_FAILURE.getMessage());
            return ResponseEntity.status(ExcelEnum.DOWNLOAD_FAILURE.getCode()).build();
        } finally {
            if (!Objects.isNull(excelWriter)) {
                excelWriter.finish();
            }
            log.info("写入Excel结束!!!");
        }
    }

    private void dealSheet(ExcelReader excelReader, int fileSheetCount) {
        for (int i = 0; i < fileSheetCount; i++) {
            log.info("当前读取第{}个sheet页的数据", i + 1);
            CustomReadListener customReadListener = new CustomReadListener();
            // headRowNumber(1)：表明前4行是表头，在customReadListener监听器中的invokeHead()方法会自动读取表头数据进行解析
            ReadSheet readSheet = EasyExcel.readSheet(i).head(CompanyVO.class).headRowNumber(4).registerReadListener(customReadListener).build();
            excelReader.read(readSheet);
            String budgetAgency = customReadListener.getBudgetCom();
            String budgetDate = customReadListener.getBudgetDate();
            List<CompanyVO> companyVOList = customReadListener.getCompanyVOList();

            if (CollectionUtils.isEmpty(companyVOList)) {
                log.info("判空1：第{}个sheet页中无数据!!!", i + 1);
                continue;
            }

            boolean flag = false;
            for (CompanyVO companyVO : companyVOList) {
                flag = companyVO.notEmpty();
            }

            // flag 为true，则表明该导入的sheet页中，至少有一行数据
            if (!flag) {
                log.info("判空2：第{}个sheet页中无数据!!!", i + 1);
                continue;
            }

            // 每次插入前需要将 预算机构+预算时间下的数据删除
            deleteData(budgetAgency, budgetDate);

            // 处理数据，并将处理后的数据入库
            dealAndInsertData(companyVOList, budgetAgency, budgetDate, i);

            log.info("第{}个sheet页的数据读取完毕!!!", i + 1);
        }
    }

    private void deleteData(String budgetAgency, String budgetDate) {
        log.info("删除数据，预算机构：{}，预算时间：{}", budgetAgency, budgetDate);
        excelMapper.deleteData(budgetAgency, budgetDate);
    }

    /**
     * 1、处理数据
     * 2、将处理后的数据入库
     * @param companyVOList
     * @param budgetAgency
     * @param budgetDate
     * @param i
     */
    private void dealAndInsertData(List<CompanyVO> companyVOList, String budgetAgency, String budgetDate, int i) {
        int tempCount = companyVOList.size() / BATCH_SIZE;
        List<CompanyVO> tempList = null;
        if (tempCount > 0) {
            tempList = new ArrayList<>(BATCH_SIZE);
        }

        log.info("开始处理第{}个sheet页中的数据", i + 1);
        for (int j = 0; j < companyVOList.size(); j++) {
            CompanyVO companyVO = companyVOList.get(j);
            if (j != 0) {
                String project = companyVO.getProject();
                if (StringUtils.isEmpty(project)) {
                    CompanyVO companyVOPre = companyVOList.get(j - 1);
                    String projectPre = companyVOPre.getProject();
                    companyVO.setProject(projectPre);
                }
            }
            companyVO.setBudgetAgency(budgetAgency);
            companyVO.setBudgetDate(budgetDate);
            log.info("第{}行数据为：{}", j + 4, companyVO);

            if (tempCount > 0) {
                tempList.add(companyVO);
            }

            if (tempCount > 0 && i == BATCH_SIZE) {
                excelMapper.insertReadExcelData(tempList);
                tempCount--;
                tempList = Lists.newArrayList();
            }
        }

        // 将剩余不足100条的数据入库
        List<CompanyVO> companyVOS = companyVOList.subList(tempCount * BATCH_SIZE, companyVOList.size());
        if (!CollectionUtils.isEmpty(companyVOS)) {
            excelMapper.insertReadExcelData(companyVOS);
        }
    }
}
